import { BaseMode,Calculator } from "./interface";
import { Utils } from "./util";

export class InterfaceTest implements BaseMode{
    run (){
        let util = new Utils(); 
        //接口       
        function printLabel(labelledObj: { label: string }) {
            util.log(labelledObj.label,"printLabel label");
            util.log(labelledObj,"printLabel all");
        }
        //check object 
        let myObj = { size: 10, label: "Size 10 Object" };
        printLabel(myObj);

        //check interface
        interface LabelledValue {
            size: number;
        }
        function iPrintLabel(labelledObj: LabelledValue) {
            util.log(labelledObj.size,"printLabel label");
            util.log(labelledObj,"printLabel all");
        }
        iPrintLabel(myObj);

        //可选属性
        interface SquareConfig {
            color?: string;
            width?: number;
            // [propName: string]: any;//索引签名
        }
        function createSquare(config: SquareConfig): {color: string; area: number} {
            let newSquare = {color: "white", area: 100};
            if (config.color) {
                newSquare.color = config.color;
            }
            if (config.width) {
                newSquare.area = config.width * config.width;
            }
            return newSquare;
        }

        let mySquare = createSquare({color: "black",width:2});
        util.log(mySquare,"createSquare");

        //只读属性
        interface Point {
            readonly x: number;
            readonly y: number;
        }
        let p1: Point = { x: 10, y: 20 };
        // p1.x = 5; // error!
        //ReadonlyArray<T>,它与Array<T>相似，只是把所有可变方法去掉了，因此可以确保数组创建后再也不能被修改
        let a: number[] = [1, 2, 3, 4];
        let ro: ReadonlyArray<number> = a;
        // ro[0] = 12; // error!
        // ro.push(5); // error!
        // ro.length = 100; // error!
        // a = ro; // error!
        // let b = ro as number[];
        let b = ro.concat([]);
        b.push(10);
        util.log(ro === a,"ReadonlyArray === []");
        util.log(b,"number[] b");
        util.log(ro,"ReadonlyArray ro as number[] result is a reference");
        // 最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。
        // 做为变量使用的话用 const，若做为属性则使用readonly。

        //额外的属性检查
        //额外检查colour字段不是接口SquareConfig里的字段，故报错
        // let mySquare2 = createSquare({ colour: "red", width: 100 });//error
        // 强制转换可以正常赋值
        let mySquare2 = createSquare({ colour: "red", width: 100 } as SquareConfig);
        util.log(mySquare2,"强制转换跳过额外的属性检查");
        // 添加索引签名[propName: string]: any;//索引签名
        // let mySquare3 = createSquare({ colour: "green", width: 100});
        // util.log(mySquare3,"索引签名可添加额外的属性");
        // 对象赋值给一个另一个变量可跳过额外的属性检查
        let squareOptions = { colour: "red", width: 100 };
        let mySquare4 = createSquare(squareOptions);
        util.log(mySquare4,"对象赋值给一个另一个变量可跳过额外的属性检查");


        //函数类型
        interface SearchFunc {
            (source: string, subString: string): boolean;
        }
        let mySearch: SearchFunc;
        //函数类型接口 参数名称可以不一样，但是参数顺序必须一样
        mySearch = function(source: string, subString: string) {
            let result = source.search(subString);
            if (result == -1) {
                return false;
            }
            else {
                return true;
            }
        }


        //可索引的类型
        class Animal {
            name: string;
        }
        class Dog extends Animal {
            breed: string;
            constructor(name:string) {
                super();
                this.name = name;
                this.breed = `hello ${name}!`;
            }           
        }

        // Error: indexing with a 'string' will sometimes get you a Dog!
        // 共有支持两种索引签名：字符串和数字。 
        // 可以同时使用两种类型的索引，但是数字索引的返回值必须是字符串索引返回值类型的子类型。
        interface NotOkay {
            [x: number]: Dog;
            // [x: string]: Animal;
        }

        let tArray: NotOkay = [new Dog("aa"), new Dog("bb")];
         util.log(tArray,"Dog array interface");
        //  util.log(tArray["1"],"Dog array interface");

        //字符串索引签名能够很好的描述dictionary模式

        //索引签名设置为只读，这样就防止了给索引赋值
        interface ReadonlyStringArray {
            readonly [index: number]: string;
        }
        let myArray: ReadonlyStringArray = ["Alice", "Bob"];
        // myArray[2] = "Mallory"; // error!
        util.log(myArray,"ReadonlyStringArray");
        util.log(myArray[1],"ReadonlyStringArray");
        

        //类静态部分与实例部分的区别
        interface ClockConstructor {
            new (hour: number, minute: number): ClockInterface;
        }
        interface ClockInterface {
            tick():void;
        }

        function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
            return new ctor(hour, minute);
        }

        class DigitalClock implements ClockInterface {
            constructor(public h: number, public m: number) { 
                console.log("DigitalClock constructor",h,m);
                // this.h = h;
                // this.m = m;
            }
            tick() {
                console.log("beep beep",this.h,this.m);
            }
        }
        class AnalogClock implements ClockInterface {
            constructor(public h: number, public m: number) { }
            tick() {
                console.log("tick tock",this.h,this.m);
            }
        }
        class TestClock  {
            // constructor(public h: number, public m: number) { }
            tick1() {
                console.log("TestClock TestClock");
            }
        }
        let digital = createClock(DigitalClock, 12, 17);
        digital.tick();
        let analog = createClock(AnalogClock, 7, 32);
        analog.tick();
        // let testClock = createClock(TestClock, 7, 32);
        // testClock.tick();

        //扩展接口
        //和类一样，接口也可以相互扩展。 这让我们能够从一个接口里复制成员到另一个接口里，可以更灵活地将接口分割到可重用的模块里。
        interface Shape {
            color: string;
        }

        interface Square extends Shape {
            sideLength: number;
        }

        interface PenStroke {
            penWidth: number;
        }
        
        // let square = <Square>{};
        let square = {} as Square;
        // let square:Square = {};
        square.color = "blue";
        square.sideLength = 10;
        util.log(square,"extends interface square");
        interface Square extends Shape, PenStroke {
            sideLength: number;
        }      
        square.color = "blue";
        square.sideLength = 10;
        square.penWidth = 5.0;
        util.log(square,"multi extends interface square");

        //mixed type interface
        interface Counter {
            (start: number): string; //function type;
            interval: number; //number;
            reset(): void; //function
        }

        function getCounter(): Counter {
            // let counter = <Counter>function (start: number) { };           
            let counter = function(start: number){
                util.log(start,"interface function type");
            } as Counter;
            // counter.constructor = function (start: number) { };        
            counter.interval = 123;
            counter.reset = function () { 
                util.log("getCounter reset","interface function");
            };
            return counter;
        }
        let c = getCounter();
        c(10);
        c.reset();
        c.interval = 5.0;

        // 接口继承类
        class Control {
            private state: any;
        }

        interface SelectableControl extends Control {
            select(): void;
        }

        // TODO: why?
        // Button和TextBox类是SelectableControl的子类（因为它们都继承自Control并有select方法）....??
        class Button extends Control {
            select() { }
        }

        class TextBox extends Control {
            select() { }
        }

        class Image {
            select() { }
        }

        class Location {
            select() { }
        }
        
    }; 
}



export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}
// 导出原先的验证器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./interface";

class ProgrammerCalculator extends Calculator {
    static digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];

    constructor(public base: number) {
        super();
        if (base <= 0 || base > ProgrammerCalculator.digits.length) {
            throw new Error("base has to be within 0 to 16 inclusive.");
        }
    }
    protected processDigit(digit: string, currentValue: number) {
        if (ProgrammerCalculator.digits.indexOf(digit) >= 0) {
            return currentValue * this.base + ProgrammerCalculator.digits.indexOf(digit);
        }
    }
}

// Export the new extended calculator as Calculator
export { ProgrammerCalculator as Calculator };
// Also, export the helper function
export { test } from "./interface";